package com.sfeir.tv.client.mvc.framework;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Controller extends AbstractInitializable
{
	private Model model;
	private View view;
	private Controller parent;
	private List childrenController = new ArrayList();

	/**
	 * Map events
	 * key: an event type
	 * value: a list of listener for the given event type
	 */
    Map events = new HashMap();
	
	public Model getModel() {
		return model;
	}

	public void setModel(Model model) {
		this.model = model;
	}

	public View getView() {
		return view;
	}

	public void setView(View view) {
		this.view = view;
	}

	public Controller getParent() {
		return parent;
	}

	public void setParent(Controller parent) {
		this.parent = parent;
	}

	public void addChildController(Controller childController) {
		this.childrenController.add(childController);
	}

	public void removeChildrenController(Controller childController) {
		this.childrenController.remove(childController);
	}

	// Firing events and notifying listeners algorithm:
	// The event goes up the chain of parent controller until it reaches the first parent. 
	// and then notification is sent down to all child controller.
	// Default behavior.
    public void fireEvent(final AppEvent event)
    {
        // TODO: What if a different strategy applies?
        fireEvent( event, false );
    }

    public void fireEvent(final AppEvent event, final boolean fireDownOnly)
    {
        // TODO: What if a different strategy applies?
        if ( !fireDownOnly && parent != null )
            parent.fireEvent( event );
        else
            fireEventDown( event );
    }

    public final void fireEventDown( final AppEvent event )
    {
        // TODO: What if a different strategy applies?
        fireEventToChildren(event);
        notifyEventListeners(event);
    }

    protected void fireEventToChildren(final AppEvent event)
    {
        for (int i = 0; i < childrenController.size(); i++)
            notifyListenersAndRecurse(((Controller) childrenController.get(i)), event);
    }

    private void notifyListenersAndRecurse(final Controller controller, final AppEvent event)
    {
        // TODO: What if a different strategy applies?
        controller.notifyEventListeners(event);
        controller.fireEventToChildren(event);
    }

    private void notifyEventListeners(final AppEvent event)
    {
        final List customCollection = (List)events.get( Integer.toString( event.getEventType() ) );
        
        if( customCollection == null ) return;
        
        for (int i = 0; i < customCollection.size(); i++)
            ((AppEventListener) customCollection.get( i )).handleAppEvent(event);
    }

    public void register(final int eventType, final AppEventListener listener) 
    {
        addMapEntryIfItDoesNotExist( eventType );
        addListenerToMapEntry( eventType, listener );
    }

    private void addMapEntryIfItDoesNotExist(final int eventType)
    {
        if ( !events.containsKey( Integer.toString( eventType ) ) )
            events.put( Integer.toString( eventType ), new ArrayList() );
    }

    private void addListenerToMapEntry(int eventType, final AppEventListener listener)
    {
        ((List)events.get( Integer.toString( eventType ) )).add( listener );
    }

    public void deregister(final int eventType, final AppEventListener listener)
    {
        if( events.containsKey( Integer.toString( eventType ) ) )
            ((List)events.get( Integer.toString( eventType ) )).remove(listener);
    }

    public boolean isNull()
    {
        return false;
    }
}
